// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//


#pragma once

#include <cstdint>
#include <pollux/common/base/bit_util.h>

namespace kumo::pollux {
    template<typename T>
    class Range {
    public:
        Range(const void *data, int32_t begin, int32_t end)
            : data_(reinterpret_cast<const T *>(data)), begin_(begin), end_(end) {
        }

        const T *data() const {
            return data_;
        }

        const uint64_t *bits() const {
            return reinterpret_cast<const uint64_t *>(data_);
        }

        int32_t begin() const {
            return begin_;
        }

        int32_t end() const {
            return end_;
        }

        T operator[](int32_t idx) const {
            return data_[begin_ + idx];
        }

    private:
        const T *data_;
        int32_t begin_;
        int32_t end_;
    };

    template<>
    inline bool Range<bool>::operator[](int32_t idx) const {
        return bits::isBitSet(reinterpret_cast<const uint64_t *>(data_), begin_ + idx);
    }

    template<typename T>
    class WritablePosition {
    public:
        WritablePosition(uint64_t *pointer, int8_t bitIndex)
            : pointer_(
                reinterpret_cast<uint64_t>(pointer) |
                (static_cast<uint64_t>(bitIndex) << 56)) {
        }

        explicit WritablePosition(T *pointer)
            : pointer_(reinterpret_cast<uint64_t>(pointer)) {
        }

        operator T() const {
            return *reinterpret_cast<T *>(pointer_);
        }

        T operator=(T value) const {
            return *reinterpret_cast<T *>(pointer_) = value;
        }

    private:
        uint64_t *bitsPointer() const {
            return reinterpret_cast<uint64_t *>(pointer_ & ((1LL << 56) - 1));
        }

        int32_t bitPosition() const {
            return pointer_ >> 56;
        }

        uint64_t pointer_;
    };

    template<>
    inline WritablePosition<bool>::operator bool() const {
        return *bitsPointer() & (1L << bitPosition());
    }

    template<>
    inline bool WritablePosition<bool>::operator=(bool value) const {
        if (value) {
            *bitsPointer() |= 1L << bitPosition();
        } else {
            *bitsPointer() &= ~(1L << bitPosition());
        }
        return value;
    }

    template<typename T>
    class MutableRange {
    public:
        MutableRange(T *data, int32_t begin, int32_t end)
            : data_(data), begin_(begin), end_(end) {
        }

        MutableRange(void *data, int32_t begin, int32_t end)
            : MutableRange(reinterpret_cast<T *>(data), begin, end) {
        }

        T *data() const {
            return data_;
        }

        uint64_t *bits() {
            return reinterpret_cast<uint64_t *>(data_);
        }

        int32_t begin() const {
            return begin_;
        }

        int32_t end() const {
            return end_;
        }

        WritablePosition<T> operator[](int32_t idx) {
            return WritablePosition<T>(&data_[begin_ + idx]);
        }

    private:
        T *data_;
        int32_t begin_;
        int32_t end_;
    };

    template<>
    inline WritablePosition<bool> MutableRange<bool>::operator[](int32_t idx) {
        int32_t bit = begin_ + idx;
        return WritablePosition<bool>(
            reinterpret_cast<uint64_t *>(data_) + (bit / 64),
            static_cast<int8_t>(bit & 63));
    }
} // namespace kumo::pollux
